<?php
/**
 * In this file the class '\UF\Translator' and some functions are defined.
 *
 * @category   UniKap-Framework
 * @package    Core
 * @author     Ulf -UniKado- Kadner <ulfikado@gmail.com>
 * @since      2014-03-29 16:17
 * @version    0.1
 */

namespace UF
{

    # <editor-fold defaultstate="collapsed" desc="CLASS 'Translator'">

    /**
     * This class defines a object, to access some translation data in a predefined format.
     *
     * The class works as a singleton like follow:
     *
     * You can define one or more folders that contain some localized translation data (translation folder)
     * The first folder is defined automatic and is used by Framework for internal translations. Dont remove it or
     * the framework does not use localized data.
     *
     * Every translation folder must following a predefined structure:
     *
     * <pre>
     * [translation folder]
     *   |
     *   +- [de_DE]
     *   |    |
     *   |    +- namespace1.php
     *   |    +- namespace2.php
     *   |    +- …
     *   |
     *   +- [nl]
     *   |    |
     *   |    +- namespace1.php
     *   |    +- namespace2.php
     *   |    +- …
     *   +- […]
     * </pre>
     *
     * You have to define for every loacale a separate folder. If there is no need to specify translations for a
     * specific country/region you can only use the LID (Language ID) als folder name.
     *
     * For every locale you can define one or more namespaces as separate PHP files wich contains the translation for
     * a specific application range. If you use more namespace it reduces more the overhead of loaded data, because
     * the translation data are only loaded if they are needed.
     *
     * The format of the namespace files is:
     *
     * <code>
     * <?php
     *
     * return array(
     *
     *     'Origin string…' => 'Originale Zeichenkette…',
     *     'Its me!'        => 'Das bin ich!'
     *     // more...
     *
     * );
     *
     * ?>
     * </code>
     *
     * So all translation data must be returned as a associative Array.
     *
     * But have attention with the translation namespaces! Every namespace can be used only one time for whole
     * application! The framework internal namespace all starts with a single underscore '_'.
     *
     * For easier use the functions \UF\_(), \UF\_0, \UF\_1, \UF\_2, \UF\_3, \UF\_4 are defined.
     *
     * Ein Beispielanwendung könnte so aussehen:
     *
     * <code>
     * <?php
     *
     * // First you have to configure out the Locale if there is a need for it.
     * // After its done you have to define your translation folder
     *
     * \UF\Translator::GetInstance()->addTranslationFolder('/foo/bar/baz');
     * // Its the same with the function
     * #\UF\add_translation_folder('/foo/bar/baz');
     *
     * // If you need a translated string in you application you can access it by
     *
     * $translated = \UF\_( 'Namespace', 'Im a string to translate :-)' );
     *
     * // If the defined namespace is not loaded it will be loaded automaticly.
     *
     * // If a translated string should contains some placeholders in (s)printf style the \UF\_1-4 functions can
     * // used.
     *
     * $translated = \UF\_1( 'Namespace', 'Let me give you %d eggs.', 20 );
     *
     * // or:
     *
     * $translated = \UF\_2( 'Namespace',
     *     'Let me give you %d eggs and %s bred.', 20, 'some' );
     *
     * // or:
     *
     * $translated = \UF\_3( 'Namespace',
     *     'Let me give you %d eggs and %s bred with %d gram buther.',
     *     20, 'some', 5 );
     *
     * </code>
     *
     * @since  v0.1
     */
    final class Translator
    {

        # <editor-fold defaultstate="collapsed" desc="- - - -   P R I V A T E   F I E L D S   - - - - - - - - - - - - - - - - - - - - -">

        /**
         * This array contains all currently usable translation folders.
         *
         * @var    array
         * @since  v0.1
         */
        private $sourcefolders;

        /**
         * Array with all currently loaded translations of a language.
         *
         * @var    array
         * @since  v0.1
         */
        private $data;

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P R I V A T E   S T A T I C   F I E L D S   - - - - - - - - - - - - - -">

        /**
         * Holds the singleton instance of this class.
         *
         * @var    \UF\Translator
         * @since  v0.1
         */
        private static $instance = null;

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - >   H I D D E N   C O N S T R U C T O R   - - - - - - - - - - - - - - - - -">

        /**
         * Private constructor because its a singleton.
         *
         * @since  v0.1
         */
        private function __construct()
        {
            $this->data = array();
            $this->sourcefolders = array();
        }

        /**
         * Disable cloning, because its a singleton.
         *
         * @since  v0.1
         */
        private function __clone() { }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   S T A T I C   M E T H O D S   - - - - - - - - - - - - - -">

        /**
         * Returns the Translator singleton instance and creates it by need.
         *
         * @return \UF\Translator
         * @since  v0.1
         */
        public static function GetInstance()
        {
            if ( \is_null( self::$instance ) ) self::$instance = new \UF\Translator();
            return self::$instance;
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - -">

        /**
         * Returns the translation of the defined string for defined namespace.
         *
         * @param  string $namespace The translation namespace that defines the translation
         * @param  string $str The string to translate
         * @return string Returns the translated string, or $str if no translation exists.
         * @since  v0.1
         */
        public function get( $namespace, $str )
        {
            if ( ! isset( $this->data[$namespace] ) ) $this->loadAdd( $namespace );
            elseif (  1 > \count( $this->data[$namespace] ) ) $this->loadAdd( $namespace );
            if ( ! isset( $this->data[$namespace][$str] ) ) return $str;
            return $this->data[$namespace][$str];
        }

        /**
         * Removes all translation data from a instance. If $namespace is defined, only the translation data for
         * this namespace are removed.
         *
         * @param  string $namespace Optional namespace, if only data for it should be removed.
         * @since  v0.1
         */
        public function clear( $namespace = null )
        {
            if ( empty( $namespace ) ) $this->data = array();
            else if ( isset ( $this->data[$namespace] ) ) unset ( $this->data[$namespace] );
        }

        /**
         * Adds a translation folder.
         *
         * The folder structure should looks like:
         *
         * <pre>
         * [translation folder]
         *   |
         *   +- [de_DE]
         *   |    |
         *   |    +- namespace1.php
         *   |    +- namespace2.php
         *   |    +- …
         *   |
         *   +- [nl]
         *   |    |
         *   |    +- namespace1.php
         *   |    +- namespace2.php
         *   |    +- …
         *   +- […]
         * </pre>
         *
         * @param  string $folder The folder that contains the namespace files with the translations.
         * @return boolean TRUE on success, FALSE if $folder dont exists, or is allready registered
         * @since  v0.1
         */
        public function addTranslationFolder( $folder )
        {
            if ( \in_array( $folder, $this->sourcefolders ) ) return false;
            if ( !\is_dir( $folder ) ) return false;
            $this->sourcefolders[] = $folder;
            return true;
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P R I V A T E   M E T H O D S   - - - - - - - - - - - - - - - - - - - -">

        /**
         * @since  v0.1
         */
        private function loadAdd( $namespace )
        {
            foreach ( $this->sourcefolders as $sf )
            {
                $lcf = \rtrim( $sf, '\\/' ) . \DIRECTORY_SEPARATOR . (string) \UF\Locale::GetInstance();
                if ( !\is_dir( $lcf ) )
                {
                    $lcf = \rtrim( $sf, '\\/' ) . \DIRECTORY_SEPARATOR . \UF\Locale::GetInstance()->getLID();
                    if ( !\is_dir( $lcf ) ) continue;
                }
                $lcff = $lcf . \DIRECTORY_SEPARATOR . $namespace . '.php';
                if ( !\file_exists( $lcff ) ) continue;
                $translations = include $lcff;
                if ( empty( $translations ) ) continue;
                $this->data[$namespace] = $translations;
                break;
            }
            if ( !isset( $this->data[$namespace] ) ) $this->data[$namespace] = array();
        }

        # </editor-fold>

    }

    # </editor-fold>

    # <editor-fold defaultstate="collapsed" desc="FUNCTIONS '_' + '_0' + '_1' + '_2' + '_3' + '_4'">

    /**
     * Returns the translation of the string for defined namespace. For translation the current Locale is used.
     *
     * @param  string $namespace The translation namespace.
     * @param  string $string The string to translate
     * @return string Translated string, or origin string if no tranlation exists.
     * @since  v0.1
     */
    function _( $namespace, $string )
    {
        return \UF\Translator::GetInstance()->get( $namespace, $string );
    }

    /**
     * Returns the translation of the string for defined namespace. For translation the current Locale is used.
     *
     * @param  string $namespace The translation namespace.
     * @param  string $string The string to translate
     * @return string Translated string, or origin string if no tranlation exists.
     * @since  v0.1
     */
    function _0( $namespace, $string )
    {
        return \UF\Translator::GetInstance()->get( $namespace, $string );
    }

    /**
     * Returns the translation of the string for defined namespace with the use of sprintf.
     * For translation the current Locale is used.
     *
     * @param  string $namespace The translation namespace.
     * @param  string $string The string to translate
     * @param  mixed  $var The first replacement
     * @return string Translated string, or origin string if no tranlation exists.
     * @since  v0.1
     */
    function _1( $namespace, $string, $var )
    {
        return \sprintf( \UF\Translator::GetInstance()->get( $namespace, $string ), $var );
    }

    /**
     * Returns the translation of the string for defined namespace with the use of sprintf.
     * For translation the current Locale is used.
     *
     * @param  string $namespace The translation namespace.
     * @param  string $string The string to translate
     * @param  mixed $var1 The first replacement
     * @param  mixed $var2 The second replacement
     * @return string Translated string, or origin string if no tranlation exists.
     * @since  v0.1
     */
    function _2( $namespace, $string, $var1, $var2 )
    {
        return \sprintf( \UF\Translator::GetInstance()->get( $namespace, $string ), $var1, $var2 );
    }

    /**
     * Returns the translation of the string for defined namespace with the use of sprintf.
     * For translation the current Locale is used.
     *
     * @param  string $namespace The translation namespace.
     * @param  string $string The string to translate
     * @param  mixed $var1 The first replacement
     * @param  mixed $var2 The second replacement
     * @param  mixed $var3 The third replacement
     * @return string Translated string, or origin string if no tranlation exists.
     * @since  v0.1
     */
    function _3( $namespace, $string, $var1, $var2, $var3 )
    {
        return \sprintf( \UF\Translator::GetInstance()->get( $namespace, $string ), $var1, $var2, $var3 );
    }

    /**
     * Returns the translation of the string for defined namespace with the use of sprintf.
     * For translation the current Locale is used.
     *
     * @param  string $namespace The translation namespace.
     * @param  string $string The string to translate
     * @param  mixed $var1 The first replacement
     * @param  mixed $var2 The second replacement
     * @param  mixed $var3 The third replacement
     * @param  mixed $var4 The fourth replacement
     * @return string Translated string, or origin string if no tranlation exists.
     * @since  v0.1
     */
    function _4( $namespace, $string, $var1, $var2, $var3, $var4 )
    {
        return \sprintf( \UF\Translator::GetInstance()->get( $namespace, $string ), $var1, $var2, $var3, $var4 );
    }

    # </editor-fold>

    # <editor-fold defaultstate="collapsed" desc="FUNCTION 'add_translation_folder'">

    /**
     * Adds a translation folder.
     *
     * The folder structure should looks like:
     *
     * <pre>
     * [translation folder]
     *   |
     *   +- [de_DE]
     *   |    |
     *   |    +- namespace1.php
     *   |    +- namespace2.php
     *   |    +- …
     *   |
     *   +- [nl]
     *   |    |
     *   |    +- namespace1.php
     *   |    +- namespace2.php
     *   |    +- …
     *   +- […]
     * </pre>
     *
     * @param  string $folder
     * @return boolean TRUE on success, FALSE if $folder dont exists, or is allready registered
     * @since  v0.1
     */
    function add_translation_folder( $folder )
    {
        return \UF\Translator::GetInstance()->addTranslationFolder( $folder );
    }

    # </editor-fold>

}

